package com.moma.chatting.core;

import java.util.HashMap;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.moma.chatting.exception.*;

public class ChattingManager {
	
	private long recycleInterval = 15000;
	public void setRecycleInterval(long value) throws InvalidOperationException{
		if (this.isInitalized)
			throw new InvalidOperationException("Cannot change recycleInterval after manager initialized...");
		this.recycleInterval = value;
	}
	private long observeInterval = 30000;
	public void setObserveInterval(long value) throws InvalidOperationException{
		if (this.isInitalized)
			throw new InvalidOperationException("Cannot change observeInterval after manager initialized...");
		this.observeInterval = value;
	}
	private boolean isInitalized = false;
	private HashMap<Integer, Conversation> conversationMap;
	private BlockingQueue<Message> messageQueue;
	
	private MessageDispatcher dispatcher;
	private ConversationObserver observer;
	private ConversationRecycler recycler;
	
	private Thread messageDispatcherWroker;
	private Thread conversationObserverWorker;
	private Thread conversationRecyclerWroker;
	
	private final static Logger logger;
	
	static{
		logger = LoggerFactory.getLogger(ChattingManager.class);
	}
	
	private static class SingletonHolder {
		private static final ChattingManager instance = new ChattingManager();
	}
	
	public static final ChattingManager getInstance(){
		return SingletonHolder.instance;
	}
	private ChattingManager(){
		// nothing to do.
	}
	
	public void initialize(){
		if (!this.isInitalized){
			
			this.conversationMap = new HashMap<Integer, Conversation>();
			this.messageQueue = new ArrayBlockingQueue<Message>(1000);
			this.dispatcher = new MessageDispatcher(messageQueue, conversationMap);
			this.observer = new ConversationObserver(conversationMap,this.observeInterval);
			this.recycler = new ConversationRecycler(conversationMap,this.recycleInterval);
			
			this.messageDispatcherWroker = new Thread(dispatcher,"MessageDispatcher");
			this.conversationObserverWorker = new Thread(observer, "ConversationObserver");
			this.conversationRecyclerWroker = new Thread(recycler, "ConversationRecycler");
			
			this.messageDispatcherWroker.start();
			this.conversationObserverWorker.start();
			this.conversationRecyclerWroker.start();
			this.isInitalized = true;
			
			logger.info("MessagingManager is initialized...");
		}
	}
	
	public void pushMessage(Message msg){
		try {
			logger.info(String.format("Message is pushed to cache, will be dispatched to conversation %d...", msg.getConversationId()));
			this.messageQueue.put(msg);
		} catch (Exception e) {
			logger.error("Push message throws exception..." + e.getStackTrace());
		}
	}
	
	public Message[] recvMessage(String userUniqueName, int conversationId) throws UserExpiredException{
		Conversation conv = null;
		synchronized(this.conversationMap){ // sync with Dispatcher.
			conv = this.conversationMap.get(conversationId);
		}
		Message[] msgArray = null;
		try {
			msgArray = conv.retrieveMessage(userUniqueName);
		} catch (InterruptedException e1) {
			logger.error(String.format("Receive message for user %s and conversation %d throws exception...", userUniqueName, conversationId));
		}
		return msgArray;
	}
	
	public void keepAlive(String userUniqueName, int conversationId){
		logger.trace("Enter keep alive method...");
		User user = null;
		Conversation conv = null;
		synchronized(this.conversationMap){
			conv = this.conversationMap.get(conversationId);
			user = conv.getGroup().getUser(userUniqueName);
		}
		user.reportOnline();
//	    conv.reportUserAlive(userUniqueName);
		logger.trace("Leaving keep alive method...");
	}
	
	public Conversation startConversation(User[] users){
//		logger.trace("Enter create conversation method...");
//		Conversation conv = new Conversation();
//		logger.info(String.format("Conversation %d is created...", conv.getConversationId()));
		Conversation conv = Conversation.createConversation(users);
		synchronized(this.conversationMap){
			this.conversationMap.put(conv.getConversationId(), conv);
		}
		return conv;
//		int conversationId = conv.getConversationId();
//		return conversationId;
//		for (User user : users){
//			try {
//				conv.addUser(user);
//				logger.info(String.format("User %s is added to conversation %d...", user.getUniqueName(), conv.getConversationId()));
//			} catch (Exception e) {
//				// TODO Auto-generated catch block
//				logger.error("Create conversation throws exception..." + e.getStackTrace());
//			}
//		}
//		int conversationId = conv.getConversationId();
//		logger.trace("Leaving create conversation method...");
//		return conversationId;
	}
}

